![]() Acrobat file (305K) |
![]() ClarisWorks 4 file (65K) |
![]() not available yet |
Technote 1053 | JULY 1996 |
This Technote discusses GraphicsBug, the GX debugger application. It provides a description of the history of GraphicsBug, as well as an explanation of how GraphicsBug can best be used by GX developers. This Note also includes a list of all the currently known bugs in GraphicsBug.
This Note is intended for Macintosh QuickDraw GX developers who are developing applications with QuickDraw GX version 1.1.3 or earlier.
GraphicsBug is a quirky utility that provides a Macsbug-like view of Quickdraw GX applications. Although it may look like an application itself, don't let the menus and windows fool you; GraphicsBug is best used sparingly as a command-line debugger, not a cut-and-paste text program. GraphicsBug is aptly named: it's full of bugs. Still, it can be an indispensable tool when GX isn't working the way you expect.
One simple task GraphicsBug performs well is confirming that your GX Graphics code does what you think it does. After you've executed the code, you can determine:
GraphicsBug for QuickDraw GX 1.1.3 is available on the Apple website.
When GraphicsBug launches, it opens an untitled window, as shown in
Figure
1.
Figure 1 A GraphicsBug window
GraphicsBug appears to be an ordinary application with traditional menus (1 in Figure 1). (Avoid these.) The real action happens in the Command line (2 in Figure 1) -- most commands are entered here. Always start by switching to your GX application's heap. You can choose it in the Heap menu (3 in Figure 1), or by using the Heap eXchange (hx) command.
GraphicsBug commands can be extremely terse. For instance, the following selects only the GX application that begins with the letter 'm':
hx m (turned "m" into "My GX App") Heap set to 009c3f9c "My GX App".
The content pane of the window (4 in Figure 1) is used for GraphicsBug's response to commands.
You can find the address of your application with the LC command, which stands for List Clients:
lc Client Process other &ap ApHeap Name 00a59e54 0000000000002006 00a59edc 009c3e34 009c3f9c "My GX App"
Set the heap to the address under the ApHeap column:
hx 009c3f9c Heap set to 009c3f9c "My GX App".
hd Start Length Δ Typ Busy Mstr Ptr Temp TBsy Disk Object 00abdd70 0000001c+00 d 00abdd8c b heap part block 00abdd8c 00000118+00 d 00000000 b heap header block 00abdea4 0000024e+02 d 00000000 freeFileList 00abe0f4 00000014+00 d 00236838 fontList 00abe108 0000004c+00 i 00b53c10 line 00abe154 00000064+00 i 00b53c0c transform 00abe1b8 000000c4+00 d 00000000 port 00abe27c 0000003c+00 i 00b53c08 full 00abe2b8 000000b4+00 i 00b53c04 style 00abe36c 00000038+00 i 00b53c00 ink 00abe3a4 0000004c+00 i 00b53bfc line 00abe3f0 00095720 f 00000000 free block 00b53b10 00000110+00 d 00abdd8c b master pointer block 00b53c20 00000010+00 d 00abdd8c b heap trailer block Total Blocks Total of Block Sizes Free 00000001 # 1 00095720 # 612128 Direct 00000003 # 3 00000328 # 808 Indirect 00000006 # 6 00000224 # 548 Sub Heaps 00000000 # 0 00000000 # 0 Heap Size 0000000a # 10 00095ec0 # 614080
Heap dumps include all blocks allocated by GX, including undocumented internal blocks.
The details of what's in a heap dump are covered in the next section, but there are a few generalities worth noting:
The easiest way to start a GX heap survey is to get a list of all of the shapes allocated by the application. A number of the commands can have a "shape" qualifier: this restricts the blocks listed to the shapes the application has created. For instance, this code:
gxLine data = {{0, 0}, {ff(125), 0}}; gxShape line1 = GXNewLine(&data);
generates:
hd shape Start Length Δ Typ Busy Mstr Ptr Temp TBsy Disk Object 00a45ac8 0000004c+00 i 00adb5d0 line Total Blocks Total of Block Sizes Blocks 00000001 # 1 0000004c # 76
Generating one shape in the source code resulted in one shape in the dump; perfectly reasonable. But look what this command tells us:
hd line Start Length Δ Typ Busy Mstr Ptr Temp TBsy Disk Object 00a45ac8 0000004c+00 i 00adb5d0 line 00a45d64 0000004c+00 i 00adb5bc line Total Blocks Total of Block Sizes Blocks 00000002 # 2 00000098 # 152
The other line is the default shape that's used internally by GX. There are a host of circumstances where GX creates internal shapes; the "shape" attribute winnows those internal shapes out.
There are a host of ways to view the contents of a GX object. In this case, the da command (for "display all") is handy:
da line shape Displaying line gxShape from 00a45ac8 devShape nil owners 1 seed 0 flags 0 attributes no attributes gxStyle 00a45c78 gxInk 00a45d2c gxTransform 00a45b14 tagList nil cacheList nil geo.flags 0 fillType openFrameFill { 0.0000, 0.0000} { 125.0000, 0.0000}
Like hd, the da command can be used with or without modifiers. More than one modifier acts as an additional qualifier; only objects that meet all the conditions are listed.
You can use the Display Memory (dm) command to display an object if you know the address. (This was the first command created for GraphicsBug.)
dm 00a45d64 t Displaying line gxShape from 00a45d64 devShape nil owners 1 seed 0 flags isDefaultShape attributes no attributes gxStyle 00a45c78 gxInk 00a45d2c gxTransform 00a45b14 tagList nil cacheList nil geo.flags 0 fillType openFrameFill { 0.0000, 0.0000} { 0.0000, 0.0000}
The 't' stands for "typed". Another way to display an object is to option-double click on the address.
The address you have may either be in the object or just associated with it. You can use the WHere command to determine if the address is in any object.
wh 00a45aa8 Address 00a45aa8 is in the heap at 00a4567c "My Graphics App". It is 00000000 bytes into this heap block: Start Length Δ Typ Busy Mstr Ptr Temp TBsy Disk Object 00a45aa8 000000c4+00 d 00000000 port
The Find command can be used to determine what objects are associated with an address. Here's a heap dump fragment that shows the address of an ink.
Start Length Δ Typ Busy Mstr Ptr Temp TBsy Disk Object 00a45c5c 00000038+00 i 00adb4f0 ink
You can use Find to return objects that have references to the ink. Type 'f', then hold down the Command key and click on the "Mstr Ptr" value to copy it to the command line. (If the Command short-cut is stubborn, try clicking in the command line before clicking on the number to copy.)
In this example, there are three references to the ink: the line shape created by the application, the internal default line, and the ink itself. There may be more shapes that use this ink that are not shown by this command. GraphicsBug only searches the RAM-resident graphics heap and does not search the disk-based backing store (where object unloaded to disk reside).
You can qualify Find just like Heap Dump. Here, we request all user allocated shapes that refer to the ink in question:
This completes the indispensable portion of GraphicsBug. If you forget what you've learned so far, just remember to type '?'. This returns a summary of the GraphicsBug commands.
This section spells out what the commands do and how GraphicsBug works.
HX addr | <heapname>
Switches to the heap containing addr, or named <heapname>. This command works reasonably well from the command line or from the menu. It's OK to quote the <heapname>. If there's no debugging INIT, you can use the LC command to find the heap address of your GX application. You'll want to use HX to select the correct heap before using the other GraphicsBug commands.
DA [<type> ... ] [shape]
Displays all blocks in the heap, or all that match parameters.
DM addr t
Display memory from addr using the appropriate template for that type. Or, option double click on the address to display memory using a template.
F addr [<type> ... ] [shape]
Finds references to addr in the heap parts that match parameters.
HD [<type> ... ] [shape]
Dumps the heap, or the heap parts that match parameters.
WH addr
Displays the block containing addr.
Simple commands like cut-and-paste do not entirely work. GraphicsBug does a terrible job of maintaining the current selection, for instance. There are a few reliable techniques worth knowing:
DM [addr [length]]
The DisplayMemory command can produce ordinary memory dumps.
ER number
er ffff96EC graphicsWarning:contour out of range er -#26900 graphicsWarning:contour out of range
Putting the minus sign in front of the number won't work:
er #-26900 :(may be a Macintosh file system error)
What #-26900 evaluates to is anybody's guess.
F addr [number [start [end]]]
You can refine the Find command by specifying the number of items to find, and the address range of those items. The number of items doesn't have any effect, but the start and end range work OK.
HC
If you have the rare bug that corrupts the GX heap, you can use HeapCheck to isolate the offending code.
DA [bu(sy) di(rect) fr(ee) i(ndirect) t(emp) u(n)b(usy)u(n)l(oaded)] F[bu(sy) di(rect) fr(ee) i(ndirect) t(emp) u(n)b(usy)u(n)l(oaded)] HD[bu(sy) di(rect) fr(ee) i(ndirect) t(emp) u(n)b(usy)u(n)l(oaded)]
You can qualify HeapDump, Find, and DisplayAll with some implementation-dependent parameters. As of this writing, all blocks are either direct, indirect or free. Indirect blocks are shapes, styles, inks, transforms, color sets and color profiles. All other blocks are direct blocks. An indirect block always has a master pointer; a direct block has a single owner containing the pointer to that block. Unlike the Memory Manager's pointer blocks, direct blocks can be relocated.
Internally, blocks may be locked down; the bu parameter lists these busy blocks. You can explicitly lock busy blocks by calling GXLockShape. GX may create temporary blocks during an operation; the t parameter lists temp blocks. Normally, you'll never see temp blocks, but while debugging a callback function, it's possible that you'll see a temp block in the GX heap. If you see a temp block outside of a GX call, you're likely looking at a GX bug.
V [addr]
Validate all blocks (no parameters) or validate a specific block.
ValidateAll does a better job than HeapCheck in looking for block corruption; while HeapCheck can only check the length of blocks and some simple pointers and flags, ValidateAll can check the flags and pointers internal to the blocks that GX allocates. ValidateAll with no parameters checks all blocks for valid contents. Unfortunately, ValidateAll with a parameter doesn't work correctly.
These commands you'll likely never need, but for the sake of completeness, here they are. The explanations that follow are sparse, but after all, the commands are practically useless.
DV 1.1.2 (built on Apr 14 1995 at 19:15:40) Graphics gestalt version0x00010100
The only thing that DisplayVersion has going for it is that you'll get a better idea of when GraphicsBug was last revised than from looking at the creation date.
FL addr [filename] Ex.: FL 0x3321A "flat shapes"
Display the stream produced by flattening this shape
Flatten performs the same work as GXFlattenShape. Here's what the output of Flatten looks like, given a reference to a line:
fl 009c4388 newObject; size: #2 (03) headerType; byte compression (80) version == 00010000; flags == fontListFlatten | fontGlyphsFlatten (01 03) newObject; size: #6 (07) [1] fontNameType; no compression (2f) (04 c8 8e 84 00 00) newObject; size: #0 (01) [1] styleType; no compression (28) newObject; size: #0 (01) [1] inkType; no compression (29) newObject; size: #0 (01) [1] transformType; no compression (2a) newObject; size: #4 (05) lineType; byte compression (83) (00 00 7d 00) newObject; size: #0 (01) trailerType; no compression (3f)
The numbers in parentheses are data. The numbers in brackets are reference counts. The numbers after the number sign are stream data sizes, not counting the stream data two byte header. The "no/byte/word compression" refers to whether the actual data is larger than the shown data. For instance, the byte data after lineType is converted into four longs by sign extending the byte to a 16 bit word, then padding the word with 16 bits of zeros to represent a Fixed.
Only data that differs from the INIT default values is written; that's why the style, ink and transform in this example have no data. The line and its companions can be represented in just 21 bytes.
You'll see more of this in Inside Macintosh: GX Environment and Utilities. If you specify a filename, GraphicsBug will save the flattened object in binary form in the file. The file type will be "flat". You can pass this file to the UnFlatten command, described later in this Technote.
GG
Display graphics globals
This command usually returns the wrong globals. To get the correct graphics globals, follow these steps instead:
lc Client Process other &ap ApHeap Name 00ae0974 0000000000002006 00ae09fc 00a4a954 00a4aabc "My GX App"
dm 00ae0974 t clientRecord at 00ae0974: nextClient nil heapStart nil heapLength 00000000 attributes 00000000 otherGlobals 00ae09fc graphicsGlobals 00a4a954 graphicsHeap 00a4aabc owner 0000000000002006 users 00000000
dm 00a4a954 t graphics globals at 00a4a954: backingStore 00a4abd4 highest write 00000512 matchingData nil hitTestSlabGlobals nil portList 00a4aee8 deviceList nil nextPortOrder 00000002 nextDeviceOrder 00000001 nextViewGroup 00000003 windowList nil flatInfo nil flatSpool nil drawShapes: defaultShapes: line 00ae092c defaultStyle 00ae0934 defaultInk 00ae0930 defaultTransform 00ae093c defaultBitmapSets: defaultPort 00000001 defaultProfile 00000000 fontList 00a4ae24 defaultFont 00000000 translatorPtr 00000000 bmDiskCache 00000000 alreadyHaveFontList false alreadyHaveFontFamilies false groupList nil
HT
HeapTotal returns the number and amount of direct, indirect and free blocks.
ht Totaling the heap at 00a4aabc (My GX App heap). Total Blocks Total of Block Sizes Free 00000001 # 1 00095720 # 612128 Direct 00000003 # 3 00000328 # 808 Indirect 00000006 # 6 00000224 # 548 Sub Heaps 00000000 # 0 00000000 # 0 Heap Size 0000000a # 10 00095ec0 # 614080
HeapTotal works, and is accurate. Unfortunately, there are few practical examples where the results are important. Because of the way GX can use MultiFinder temporary memory and the disk to store information, the result of the HeapTotal can be deceiving.
HZ
Lists the known heaps.
If you forget the name of your application (and you're running the debug init), this will help refresh your memory.
Without the debug init, only addresses will appear in response to this command.
hz 002b92b0 start (system.graphics heap) 002eb284 end 00a4aabc start (My GX App heap) 00ae0954 end
ListClients is a slightly more useful alternative command.
IG
Displays INIT globals.
global handle: 0x000cc764 global pointer: 0x000e30f0 initFileName "GXGraphics" initVRef 0xffff initDirID 0x00001592 rsrcFileRef 0x0000 debuggerInfo 0x0014d746 memoryDispatcher 0x00000000 dispatchSetTrapAddress 0x0006ca98 dispatchGetTrapAddress 0x0003598e dispatchDispatchText 0x0015be3c dispatchDispatchLine 0x0015be44 dispatchDispatchRect 0x0015be4c dispatchDispatchRRect 0x0015be54 dispatchDispatchOval 0x0015be5c dispatchDispatchArc 0x0015be64 dispatchDispatchPoly 0x0015be6c dispatchDispatchRgn 0x0015be74 dispatchDispatchBits 0x0015be7c dispatchDispatchComment 0x0015be84 patchPictTrap 0x0015be94 originalMaxApplZone 0x4080d2dc originalInitGDevice 0x000d28b4 originalSetDeviceAttribute 0x40828000 originalSetEntries 0x000182d2 originalBringToFront 0x000acfc4 originalCalcVBehind 0x000ac158 originalCleanupApplication 0x000d611a activeClientAddress 0x00149790 activeProcessAddress 0x00149798 originalLaunch 0x00026cee originalOSDispatch 0x0025b820 originalTempNewHandle 0x0025b820 activeClientAddress 0x00149790 activeProcessAddress 0x00149798 sysHeapAddress 0x0014e848 graphicsA5 0x0015747a rootCallMade 0x0000 insidePrinting 0 systemPatchesInstalled 1
These globals are used by all GX clients. The main use of InitGlobals is to reveal which traps GX patches.
LC [process]
Lists the known graphics clients.
lc Client Process other &ap ApHeap Name 00ae0974 0000000000002005 00ae09fc 00a4a954 00a4aabc "My GX App"
ListClients shows how GX connects a gxGraphicsClient to the graphics heap, the Process Manager and the internal client record.
LP
Lists the known processes, with or without a graphics client.
lp Process Process # Active Name Client 00092594 0000000000002005 00ae0974 "My GX App" 00289d24 0000000000002004 00000000 "MW Debug/MacOS 1.4" 0000c854 0000000000002003 00000000 "GraphicsBug" 0031c0b0 0000000000002002 00000000 "ClarisWorks" 00290444 0000000000002001 00000000 "CodeWarrior IDE 1.4" 00019674 0000000000002000 00000000 "Finder" 001544f0 0000000000000000 00000000 "null process"
ListProcesses unveils that this Technote was written in ClarisWorks while using an example program called My GX App under Metrowerks to generate some GX objects, which were viewed with GraphicsBug.
OG
OtherGlobals attempts to display other (generic, non-graphic) globals used by GX.
Unfortunately, this is another command that doesn't work directly. You can get the correct result though ListClient instead. In this example, a shape is accidentally disposed twice. That causes the other globals to look like:
lc Client Process other &ap ApHeap Name 00d41214 0000000000002009 00d4129c 00a672e4 00a6744c "My GX App" dm 00d4129c t generic globals at 00d4129c: lastWarning :00000000 lastError :shape access not allowed lastNotice :00000000 stickyWarning :00000000 stickyError :shape access not allowed stickyNotice :00000000 userError 00000000() userErrorRef 00000000 userWarning 00000000() userWarningRef 00000000 userNotice 00000000() userNoticeRef 00000000 ignoredWarnings 0 ignoredNotices 0 randomSeed 00000000 00000000 validation 0 checkLeafs 0 checkRoots 0 validationProcedure 00000000 validationArgumentNumber 0 validationArgumentValue 00000000 currentProcAddr 00000000 currentProcName (none) typeName (none) validationInProgress false foundError false userDebug 00000000 debugReference 00000000
A more useful way to find errors is to use the GraphicsDebugLibrary and call SetGraphicsLibraryErrors() at the beginning of your application. If you're working with an application you didn't write, however, this will do.
Q
Quits out of GraphicsBug.
UF filename [page number]
UnFlatten is the companion to FLatten. It can display the contents of a file saved by FLatten, or a printer spool file. Since the dumps of printer spool files can be huge, you can also specify a page number to Unflatten.
UF "save me" (80) headerType; byte compression (01 03) version == 00010000; flags == fontListFlatten | fontGlyphsFlatten (07) newObject; size: #6 (2f) fontNameType; no compression [1] (04 c8 8e 84 00 00) (01) newObject; size: #0 (28) styleType; no compression [1] (01) newObject; size: #0 (29) inkType; no compression [1] (01) newObject; size: #0 (2a) transformType; no compression [1] (05) newObject; size: #4 (83) lineType; byte compression (00 00 7d 00 { 0.0000, 0.0000} { 125.0000, 0.0000} (01) newObject; size: #0 (3f) trailerType; no compression Total Opcodes Total Size New headerType # 1 # 4 lineType # 1 # 6 styleType # 1 # 2 inkType # 1 # 2 transformType # 1 # 2 fontNameType # 1 # 8 trailerType # 1 # 2 Set Default all shapes # 1 # 6 grand total # 7 # 26
The numbers in parentheses are the values in the file, one byte at a time. The numbers in square brackets are the reference indices. Values in curly braces are in decimal fixed point.
Graphics objects default to referring to the last object unflattened; the line in this example refers to the simple style, ink and transform in front of it. A reference allows a shape to refer to some object other than the one immediately before it.
Use the up/down arrow keys to set the scrolling speed.
Use dot '.' to represent the last displayed address.
operators: - + * / % ^ | & [@*] ~ () numbers: . 0x $ # '' strings: ""
You can do simple math expressions in GraphicsBug, and a lot of the time they'll actually work.
If you don't enter a leading #, it's interpreted as hexadecimal.
Arithmetic with fixed numbers works by converting the number to a 32 long first.
The first example works; the second does not.
The operators available are basically the same as in Macsbug:
- unary minus or binary subtraction
+ unary plus or binary addition
* unary indirection or binary multiplication
% modulo (but only makes sense with positive numbers)
^ xor, but not Pascal type postfix indirection
@ another way to do unary indirection
100/-3
100/(0-3)
Conditional operators like >, <, >=, <=, ==, != work, too.
Characters can be used alone or in expressions.
You can enter in more than one expression on the same command line.
Multiple commands can be separated by semicolons. It's the same in Macsbug.
Here's an example that works in both:
Oliver Steele created the Monitor menu, Chris Yerga contributed many great
ideas to GraphicsBug (including DA) and Dave Good implemented the global and
gxClient debugging templates.